#include "app_main.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"

#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_bt.h"

#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_main.h"
#include "esp_gatt_common_api.h"

#define GATTS_TABLE_TAG "GATTS_TABLE_DEMO"
#define TAG "BLE_WIFI_ECU"

#include "soc/rtc.h"
#include "driver/mcpwm.h"

/* store the station info for send back to phone */
static bool gl_sta_connected = false;
static bool ble_is_connected = false;
static uint8_t gl_sta_bssid[6];
static uint8_t gl_sta_ssid[32];
static int gl_sta_ssid_len;

static const int RX_BUF_SIZE = 1024;

struct get_serial_ecuinfo ecuinfo;

/* dim a socket to connect to server */
int sock;

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

struct esp_device_config *edc;

/* --------------------- Definitions and static variables ------------------ */
//Example Configuration
#define NO_OF_ITERS                     3
#define RX_TASK_PRIO                    9

/*
    出货用ecu-wifi板txrx引脚
*/
#define TX_GPIO_NUM                     7
#define RX_GPIO_NUM                     6
/*
    测试用ecu-wifi板txrx引脚
*/
// #define TX_GPIO_NUM                     2
// #define RX_GPIO_NUM                     3

#define EXAMPLE_TAG                     "TWAI Listen Only"

#define ID_MASTER_STOP_CMD              0x0A0
#define ID_MASTER_START_CMD             0x0A1
#define ID_MASTER_PING                  0x0A2
#define ID_SLAVE_STOP_RESP              0x0B0
#define ID_SLAVE_DATA                   0x0B1
#define ID_SLAVE_PING_RESP              0x0B2

#define GPIO_INPUT_IO 9 //板载按钮引脚
#define ESP_INTR_FLAG_DEFAULT 0
#define IOSPEEDLEN 10

xQueueHandle gpio_evt_queue = NULL; //新建一个队列句柄
extern int avgrate;
static float speed = 0;

static const twai_filter_config_t f_config = {  .acceptance_code = 0x18FEF101 << 3,
                                                .acceptance_mask = 0x00000000,
                                                .single_filter = true};
static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS();
//Set TX queue length to 0 due to listen only mode
static const twai_general_config_t g_config = {.mode = TWAI_MODE_LISTEN_ONLY,
                                              .tx_io = TX_GPIO_NUM, .rx_io = RX_GPIO_NUM,
                                              .clkout_io = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED,
                                              .tx_queue_len = 0, .rx_queue_len = 5,
                                              .alerts_enabled = TWAI_ALERT_NONE,
                                              .clkout_divider = 0};

/* -------------------------------- Base Function ------------------------------------------- */
void InvertUint16(unsigned short *dBuf, unsigned short *srcBuf)
{
	int i;
	unsigned short tmp[4];
	tmp[0] = 0;
	for (i = 0; i< 16; i++)
	{
		if (srcBuf[0] & (1 << i))
			tmp[0] |= 1 << (15 - i);
	}
	dBuf[0] = tmp[0];
}

unsigned short swapShort16(unsigned short shortValue){
    return ((shortValue & 0x00FF ) <<8) | ((shortValue & 0xFF00)>>8);
}

unsigned short CRC16_MODBUS(unsigned char *data, unsigned int datalen)
{
	unsigned short wCRCin = 0xFFFF;
	unsigned short wCPoly = 0x8005;
	
	InvertUint16(&wCPoly,&wCPoly);
	while (datalen--)
	{
		wCRCin ^= *(data++);
		for(int i = 0;i < 8;i++)
		{
			if(wCRCin & 0x01)
				wCRCin = (wCRCin >> 1) ^ wCPoly;
			else
				wCRCin = wCRCin >> 1;
		}
	}
    // wCRCin = swapShort16(wCRCin);
	return (wCRCin);
}

unsigned short crctoshort(unsigned char *data){
    unsigned short crc = 0x0000;
    crc = crc | (*data);
    crc = crc << 8;
    crc = crc | (*(data+1));

    return crc;
}

/* --------------------------- Tasks and Functions -------------------------- */
static void twai_receive_task(void *arg)
{
    uint32_t canID;
    struct ecu_to_serial ets;
    unsigned short uCRC;
    twai_message_t rx_msg;

    float speed = 0;

    esp_err_t err;

    canID = (6ULL << 26) + (65265 << 8) + 1;
    while (1) {
        memset(ets.data,0,ECU_DATA_LEN);
        ets.data[0] = 0x8E;
        err = twai_receive(&rx_msg, 5000 / portTICK_PERIOD_MS);
        if(err == ESP_ERR_TIMEOUT){
            for(int i = 1;i < 8;i++){
                ets.data[i] = 0;
            }
        }else{
            if(rx_msg.identifier == canID){
                speed = (rx_msg.data[1] + (rx_msg.data[2] << 8)) / 256.0 ;
                speed = speed * 10;
                ets.data[1] = (int)speed >> 8;
                ets.data[2] = (int)speed;
            }
        }

        ets.len = TWAI_QUEUE_LEN;

        uCRC = CRC16_MODBUS(&ets.data[0],ets.len-2);
        ets.data[TWAI_QUEUE_LEN-1] = uCRC;
        ets.data[TWAI_QUEUE_LEN-2] = uCRC >> 8;

        xQueueSend(QHandle, (void *)&ets, 0);

        vTaskDelay(80 / portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}

static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data)
{
    wifi_mode_t mode;

    switch (event_id)
    {
    case WIFI_EVENT_STA_START:
        esp_wifi_connect();
        break;
    case WIFI_EVENT_STA_CONNECTED:
        led_control_flag = 2;
        gl_sta_connected = true;
        // example_set_static_ip(arg);
        break;
    case WIFI_EVENT_STA_DISCONNECTED:
        /* This is a workaround as ESP32 WiFi libs don't currently
           auto-reassociate. */
        gl_sta_connected = false;
        esp_wifi_connect();
        // tcp_disconnected();
        led_control_flag = 1;
        // wifi_disconnected();
        break;
    case WIFI_EVENT_AP_START:
        esp_wifi_get_mode(&mode);
        /* TODO: get config or information of softap, then set to report extra_info */
        if (ble_is_connected == true)
        {
            if (gl_sta_connected)
            {
                esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, NULL);
            }
            else
            {
                esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL);
            }
        }
        else
        {
            BLUFI_INFO("BLUFI BLE is not connected yet\n");
        }
        break;
    case WIFI_EVENT_SCAN_DONE:
    {
        uint16_t apCount = 0;
        esp_wifi_scan_get_ap_num(&apCount);
        if (apCount == 0)
        {
            BLUFI_INFO("Nothing AP found");
            break;
        }
        wifi_ap_record_t *ap_list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount);
        if (!ap_list)
        {
            BLUFI_ERROR("malloc error, ap_list is NULL");
            break;
        }
        ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, ap_list));
        esp_blufi_ap_record_t *blufi_ap_list = (esp_blufi_ap_record_t *)malloc(apCount * sizeof(esp_blufi_ap_record_t));
        if (!blufi_ap_list)
        {
            if (ap_list)
            {
                free(ap_list);
            }
            BLUFI_ERROR("malloc error, blufi_ap_list is NULL");
            break;
        }
        for (int i = 0; i < apCount; ++i)
        {
            blufi_ap_list[i].rssi = ap_list[i].rssi;
            memcpy(blufi_ap_list[i].ssid, ap_list[i].ssid, sizeof(ap_list[i].ssid));
        }

        if (ble_is_connected == true)
        {
            esp_blufi_send_wifi_list(apCount, blufi_ap_list);
        }
        else
        {
            BLUFI_INFO("BLUFI BLE is not connected yet\n");
        }
        esp_wifi_scan_stop();
        free(ap_list);
        free(blufi_ap_list);
        break;
    }
    default:
        break;
    }
    return;
}

static void ip_event_handler(void *arg, esp_event_base_t event_base,
                             int32_t event_id, void *event_data)
{
    wifi_mode_t mode;

    switch (event_id)
    {
    case IP_EVENT_STA_GOT_IP:
    {
        esp_blufi_extra_info_t info;

        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "static ip:" IPSTR, IP2STR(&event->ip_info.ip));

        esp_wifi_get_mode(&mode);

        memset(&info, 0, sizeof(esp_blufi_extra_info_t));
        memcpy(info.sta_bssid, gl_sta_bssid, 6);
        info.sta_bssid_set = true;
        info.sta_ssid = gl_sta_ssid;
        info.sta_ssid_len = gl_sta_ssid_len;
        if (ble_is_connected == true)
        {
            esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info);
        }
        else
        {
            BLUFI_INFO("BLUFI BLE is not connected yet\n");
        }
        break;
    }
    default:
        break;
    }
    return;
}

void init(int bps485) {
    const uart_config_t uart_config = {
        .baud_rate = bps485,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB,
    };
    // We won't use a buffer for sending data.
    uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
    uart_param_config(UART_NUM_1, &uart_config);
    uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}

static void uart_rx_task(void *arg)
{
    // unsigned char rx_buffer[RX_BUF_SIZE+1];
    ESP_LOGI(TAG, "waitting for send data");
    unsigned char wx_buffer[128];
    unsigned short uCRC;
    unsigned short suCRC;

    int rxBytes;
    while (1)
    {
        for(int i = 0;i < 3;i++){
            wx_buffer[i] = 0xFB;
        }
            
        rxBytes = uart_read_bytes(UART_NUM_1, &wx_buffer[3], 128, 30 / portTICK_RATE_MS); 

        if (0x03 == wx_buffer[3] && rxBytes > 0) {
            for(int i = 0;i < 3;i++){
                wx_buffer[i+rxBytes+3] = 0xFE;
            }

            uCRC = CRC16_MODBUS(&wx_buffer[3],rxBytes-2);
            suCRC = crctoshort(&wx_buffer[rxBytes+3-2]);

            if(uCRC == suCRC){
                for(int i = 107;i < 109;i++){
                    memset(&wx_buffer[i],0,sizeof(char));
                }
                ecuinfo.len = rxBytes+6;
                memcpy(ecuinfo.ecu_info,wx_buffer,ecuinfo.len);
                ecuinfo.tcp_flag = 1;
                ecuinfo.ble_flag = 1;
            }
        }
    }
    vTaskDelete(NULL);
}

static void uart_tx_task(void *arg)
{
    struct ecu_to_serial ets;
    BaseType_t xStatus;

    while (1)
    {
        xStatus = xQueueReceive(QHandle,(void *)&ets,30/portTICK_PERIOD_MS);

        if(xStatus != pdPASS){
            ;
        } else {
            uart_write_bytes(UART_NUM_1,ets.data,ets.len);
        }
        vTaskDelay(70/portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}

static void xUartToQueue(){
    struct ecu_to_serial ets = {
        .data = {0x03,0x00,0x0E,0x00,0x00,0x00,0xC0,0x02},
        .len = 8
    };

    while(1){
        xQueueSend(QHandle, (void *)&ets, 0);
        vTaskDelay(500/portTICK_PERIOD_MS);
    }

    vTaskDelete(NULL);
}

static void ecu_server(void *arg)
{
    int err;

    while (1)
    {
        if(ecuinfo.tcp_flag == 1){
            err = send(sock, ecuinfo.ecu_info, ecuinfo.len, 0);

            if (err < 0)
            {
                // ESP_LOGE(TAG, "Error occurred during sending: errno %d,value of sock:%d", errno, sock);
                xEventGroupSetBits(s_wifi_event_group, SOCKET_DISCONNECT_BIT);
                shutdown(sock, 0);
                close(sock);
                break;
            }
            ecuinfo.tcp_flag = 0;
        }
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
    if (sock != -1)
    {
        ESP_LOGE(TAG, "Shutting down socket and restarting...%d", sock);
        shutdown(sock, 0);
        close(sock);
    }
    vTaskDelete(NULL);
}

static void tcp_client_task()
{
    struct sockaddr_in dest_addr;

    while (1) {
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            close(sock);
            ESP_LOGE(TAG, "Unable to create socket: errno %d ,sockValue:%d", errno, sock);
        }

        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(edc->port);
        dest_addr.sin_addr.s_addr = inet_addr(edc->serip);

        int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));

        if (err != 0)
        {
            led_control_flag = 2;
            // ESP_LOGE(TAG, "Socket unable to connect: errno = %d,value err = %d", errno, err);
            shutdown(sock, 0);
            close(sock);
            xEventGroupSetBits(s_wifi_event_group, SOCKET_DISCONNECT_BIT);
            vTaskDelay(3000 / portTICK_PERIOD_MS);
            continue;
        } else {
            xEventGroupClearBits(s_wifi_event_group,SOCKET_DISCONNECT_BIT);
            led_control_flag = 3;
            ESP_LOGI(TAG, "socket Successfully connected");
            xTaskCreate(ecu_server,
                "ecu_server",
                1024*2,
                NULL,
                configMAX_PRIORITIES + 5,
                NULL);
            ESP_LOGI(TAG, "beginning to receive data");
        }
        vTaskDelay(3000 / portTICK_PERIOD_MS);
        // receive data,and send
        
        xEventGroupWaitBits(
            s_wifi_event_group,         /* 事件标志组句柄 */
            SOCKET_DISCONNECT_BIT,      /* 等待被设置的事件标志位，此处无线连接成功和无线连接断开事件时候，标志位会在回调程序中被置1 */
            pdTRUE,                     /* 选择是否清零被置位的事件标志位 */
            pdFALSE,                    /* 选择是否等待所有标志位都被设置,pdTRUE==参数2中所有标志位被置1，函数才返回（超溢出时间也返回）... */
            portMAX_DELAY               /* 设置等待时间 portMAX_DELAY */
        );                              //一直被阻塞，直到参数2有bit置为1，开始程序往下走
        
    }
    vTaskDelete(NULL);
}

void wifi_start(){
    /*  
        创建默认事件循环。 
    */
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    /*
        用于初始化tcpip协议栈
    */ 
    ESP_ERROR_CHECK(esp_netif_init());
    /* 
        创建有 TCP/IP 堆栈的默认网络接口实例绑定 station 或 AP。
    */
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    /*  将事件处理程序注册到系统事件循环 */
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler, NULL));

    wifi_config_t wifi_config = {
        .sta = {
	        .threshold.authmode = WIFI_AUTH_WPA2_PSK,
            .pmf_cfg = {
                .capable = true,
                .required = false
            },
        },
    };

    memcpy(wifi_config.sta.ssid,edc->inissid,sizeof(edc->inissid));
    memcpy(wifi_config.sta.password,edc->inipassword,sizeof(edc->inipassword));

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");
}

// GPIO中断事件
// ESP32的引脚中断并不是直接执行某一项操作，而是把中断事件发送给一个队列，然后主循环轮询处理，
void IRAM_ATTR gpio_isr_handler(void *arg)
{
    //获取中断参数
    // gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));类型应与创建队列时一致 uint32_t
    uint32_t gpio_num = (uint32_t)arg;

    // 向队列尾部发送一个队列消息，三个参数，分别是：
    // 目标队列的句柄
    // 发送数据的指针
    // 如果入队导致一个任务解锁，并且解锁的任务优先级高于当前被中断的任务，则将*pxHigherPriorityTaskWoken 设置成 pdTRUE，然后在中断退出前需要进行一次上下文切换 ，去执行被唤醒的优先级更高的任务。从 FreeRTOS V7.3.0 起，pxHigherPriorityTaskWoken 作为一个可选参数，可以设置为 NULL
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

// GPIO主任务
void ioSpeed(void *arg)
{
    struct timeval tv = {0};
    time_t currentt = 0;
    time_t sum = 0;
    time_t pastt = 0;
    int i = 0;
    // 准备一个变量，存储从队列接收到的消息
    // 类型应与创建队列时一致 uint32_t
    uint32_t io_num;
    for (;;)
    {
        // 从一个队列中接收消息并把消息从队列中删除，三个参数分别是：
        // 被读队列的句柄。这个句柄即是调用 xQueueCreate() 创建该队列时的返回值
        // 接收缓存指针。其指向一段内存区域，用于接收从队列中拷贝来的数据。数据单元的长度在创建队列时就已经被设定，所以该指针指向的内存区域大小应当足够保存一个数据单元。
        // 队列空时，阻塞超时的最大时间。如果该参数设置为 0，函数立刻返回。超时时间的单位为系统节拍周期，常量 portTICK_PERIOD_MS 用于辅助计算真实的时间，单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1，并且指定延时为 portMAX_DELAY 将导致任务无限阻塞（没有超时）
        if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY))
        {
            // 如果读取到消息，就打印出来
            gettimeofday(&tv,NULL);
            currentt = tv.tv_sec *1000 + tv.tv_usec/1000;

            if(i <= IOSPEEDLEN && i!=0){
                sum += currentt - pastt;
            }else if(i > IOSPEEDLEN){
                avgrate = sum/10;
                sum = 0;
                i = 0;
                speed = edc->ioSpeedFix / avgrate;
            }
            i++;

            pastt = tv.tv_sec *1000 + tv.tv_usec/1000;
        }
    }
}
 

void iospeed_init()
{
    /* 定义一个gpio配置结构体 */
    gpio_config_t gpio_config_structure;

    memset(&gpio_config_structure, 0, sizeof(gpio_config_structure));
    /* 初始化gpio配置结构体*/
    gpio_config_structure.pin_bit_mask = (1ULL << GPIO_INPUT_IO); /* 选择gpio2 */
    gpio_config_structure.mode = GPIO_MODE_INPUT;                   /* input模式 */
    gpio_config_structure.pull_up_en = 1;                           /* 上拉 */
    gpio_config_structure.pull_down_en = 0;                         /* 不下拉 */
    gpio_config_structure.intr_type = GPIO_INTR_POSEDGE;            /* ！<GPIO中断类型：上升沿*/
    /* 根据设定参数初始化并使能 */
    gpio_config(&gpio_config_structure);

    // 创建队列以处理来自isr的gpio事件
    // 创建一个新的队列。为新的队列分配所需的存储内存，并返回一个队列处理。两个参数，分别是：
    // 队列中包含最大项目数量。
    // 队列中每个项目所需的字节数。项目通过复制而不是引用排队，因为，所需的字节数，将复制给每个项目。队列中每个项目必须分配同样大小。
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));

    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);                                                      //安装驱动程序的GPIO ISR处理程序服务
    gpio_isr_handler_add(GPIO_INPUT_IO, gpio_isr_handler, (void *)GPIO_INPUT_IO); //为相应的GPIO引脚添加ISR处理程序

    // 创建一个任务,6个参数，分别是：
    // 任务函数
    // 任务名
    // 任务栈大小
    // 任务参数
    // 任务优先级
    // 任务句柄
    xTaskCreate(ioSpeed, "ioSpeed", 2048, NULL, 10, NULL);
}

void app_main(void)
{
    esp_err_t ret;

    edc = malloc(sizeof(struct esp_device_config));

    QHandle = xQueueCreate(5,sizeof(struct ecu_to_serial));

    iospeed_init();
    /* 
        初始化 NVS
    */
    ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    /*
        按模式释放controller内存
        esp_bt_controller_mem_release() 只能在esp_bt_controller_init() 之前或者 esp_bt_controller_deinit() 之后调用。
        初始化时调用了esp_bt_controller_mem_release()之后，使用esp_bt_controller_enable()来使用BLE才是安全的。
    */
    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
        
    /*  
        获得控制器配置选项。通过配置掩码启用了某些功能的一些选项或参数
    */
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();

    /* 
        初始化 BT controller 以分配 task 和其他资源。 
        这个函数需要在调用任何 BT 函数之前调用一次。
    */
    ret = esp_bt_controller_init(&bt_cfg);
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    /* 
        使能 BT controller 
        调用之后不能再次调用。如果需要动态改变 controller 的模式，则需要调用 esp_bt_controller_disable() 之后重新调用此函数。
    */
    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    /*
        蓝牙栈 `bluedroid stack` 包括了BT和 BLE 使用的基本的define和API
        初始化和分配蓝牙资源
        必须优先于每一个bluetooth stuff。
    */
    ret = esp_bluedroid_init();
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    /*
        启用蓝牙。
        必须在esp_bluedroid_init()之后。
    */
    ret = esp_bluedroid_enable();
    if (ret) {
        ESP_LOGE(GATTS_TABLE_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret));
        return;
    }

    /*
        注册 GATT 回调函数，回调函数具体内容会在下文说明 
        调用此函数以向BTA GATTS模块注册应用程序回调
    */
    ret = esp_ble_gatts_register_callback(gatts_event_handler);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gatts register error, error code = %x", ret);
        return;
    }

    /*
        建立蓝牙的FSM（有限状态机）
        这里使用回调函数来控制每个状态下的响应，需要将其在GATT和GAP层的回调函数注册
        esp_gattc_cb和esp_gap_cb处理蓝牙栈可能发生的所有情况，达到FSM的效果
        注册 GAP 回调函数
        这个回调函数中定义了在广播期间蓝牙设备的一些操作
    */
    ret = esp_ble_gap_register_callback(gap_event_handler);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gap register error, error code = %x", ret);
        return;
    }

    /*
        当调用esp_ble_gatts_app_register()注册一个应用程序Profile(Application Profile)，
        将触发ESP_GATTS_REG_EVT事件，
        除了可以完成对应profile的gatts_if的注册,
        还可以调用esp_bel_create_attr_tab()来创建profile Attributes 表
        或创建一个服务esp_ble_gatts_create_service()
        下面创建了BLE GATT服务，相当于1个独立的应用程序     
    */
    ret = esp_ble_gatts_app_register(ESP_APP_ID);
    if (ret){
        ESP_LOGE(GATTS_TABLE_TAG, "gatts app register error, error code = %x", ret);
        return;
    }

    /*
        设置了MTU的值(经过MTU交换，从而设置一个PDU中最大能够交换的数据量)。
        例如：主设备发出一个1000字节的MTU请求，但是从设备回应的MTU是500字节，那么今后双方要以较小的值500字节作为以后的MTU。
        即主从双方每次在做数据传输时不超过这个最大数据单元。
    */
    esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
    if (local_mtu_ret){
        ESP_LOGE(GATTS_TABLE_TAG, "set local  MTU failed, error code = %x", local_mtu_ret);
    }

    getInitFileData(edc);

    /* 
        成功建立事件组，则会返回事件组的句柄（指针） 
    */
    s_wifi_event_group = xEventGroupCreate();

    init(edc->bps485);

    wifi_start();

    //Install and start TWAI driver
    ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config));
    ESP_LOGI(EXAMPLE_TAG, "Driver installed");
    ESP_ERROR_CHECK(twai_start());
    ESP_LOGI(EXAMPLE_TAG, "Driver started");

    // start tcpClientTask
    xTaskCreate(tcp_client_task,        /* Task function. */
                "tcp_client_task",      /* String with name of task. */
                4096,                   /* Stack size in bytes. */
                NULL,                   /* Parameter passed as input of the task */
                configMAX_PRIORITIES-1, /* Priority of the task. */
                NULL);                  /* Task handle. */

    xTaskCreate(uart_tx_task, 
                "uart_tx_task", 
                1024*2, 
                NULL, 
                configMAX_PRIORITIES, 
                NULL);

    xTaskCreate(xUartToQueue, 
                "xUartToQueue", 
                1024*2, 
                NULL, 
                configMAX_PRIORITIES, 
                NULL);

    xTaskCreate(uart_rx_task, 
                "uart_rx_task",
                4096, 
                NULL, 
                configMAX_PRIORITIES, 
                NULL);

    xTaskCreate(twai_receive_task, 
                "TWAI_rx", 
                4096, 
                NULL, 
                configMAX_PRIORITIES, 
                NULL);

    // wifi - tcp connect status
    xTaskCreate(wifitcpstatus,          /* Task function. */
                "wifitcpstatus",        /* String with name of task. */
                4096,                   /* Stack size in bytes. */
                NULL,                   /* Parameter passed as input of the task */
                configMAX_PRIORITIES,   /* Priority of the task. */
                NULL);                  /* Task handle. */
}
